package com.weilive.core.code.writers;

import java.io.File;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;

import com.weilive.core.code.parsers.SqlFileParser;
import com.weilive.core.code.parsers.SqlFileParser.Field;
import com.weilive.core.code.parsers.SqlFileParser.SqlFile;
import com.weilive.core.code.parsers.SqlFileParser.Table;
import com.weilive.core.utils.FileUtils;
import com.weilive.core.utils.StringUtils;

public class SqlToServerCode {
	private static final String VERSION = "version";
	private static final String PLAYER_ID = "player_id";

	private static String tableClassName(String name) {
		String[] names = name.split("_");
		StringBuffer result = new StringBuffer(name.length() + 2);
		for (String s : names) {
			result.append(StringUtils.firstUpcase(s));
		}
		result.append("DO");
		return result.toString();
	}

	private static String tableMapperName(String name) {
		String[] names = name.split("_");
		StringBuffer result = new StringBuffer(name.length() + 3);
		for (String s : names) {
			result.append(StringUtils.firstUpcase(s));
		}
		result.append("Map");
		return result.toString();
	}

	private static String fileMapperName(String name) {
		String[] names = name.split("_");
		StringBuffer result = new StringBuffer(name.length() + 6);
		for (String s : names) {
			result.append(StringUtils.firstUpcase(s));
		}
		result.append("Mapper");
		return result.toString();
	}

	public static void writeTable(Table table, String package_name, PrintWriter mapWriter, PrintWriter javaWriter) {
		mapWriter.printf("    <resultMap id=\"%s\" type=\"%s.%s\" >\n", tableMapperName(table.name), package_name, tableClassName(table.name));
		for (Field field : table.fields) {
			mapWriter.printf("        <result property=\"%s\" column=\"%s\" />\n", field.name, field.name);
		}
		mapWriter.printf("    </resultMap>\n");
		mapWriter.printf("\n");

		javaWriter.printf("package %s;\n", package_name);
		javaWriter.printf("\n");
		if (table.hasDate) {
			javaWriter.printf("import java.util.Date;\n");
		}
		javaWriter.printf("\n");
		javaWriter.printf("public class %s {\n", tableClassName(table.name));
		javaWriter.printf("\n");
		for (Field field : table.fields) {
			javaWriter.printf("    public %s %s;%s\n", field.type, field.name, field.comment != null && field.comment.length() > 0 ? "\t// " + field.comment : "");
		}
		javaWriter.printf("\n");
		for (Field field : table.fields) {
			javaWriter.printf("    public %s %s() {\n", field.type, StringUtils.getter(field.name));
			javaWriter.printf("        return %s;\n", field.name);
			javaWriter.printf("    }\n");
			javaWriter.printf("\n");

			javaWriter.printf("    public void %s(%s %s) {\n", StringUtils.setter(field.name), field.type, field.name);
			javaWriter.printf("        this.%s = %s;\n", field.name, field.name);
			javaWriter.printf("    }\n");
			javaWriter.printf("\n");
		}
		javaWriter.printf("}\n");
	}

	public static void writeAtClassDir(String file, String package_name) throws Exception {
		SqlFile sqlFile = SqlFileParser.parse(new File(file));
		String path = FileUtils.getClassDir() + package_name.replace(".", "/") + "/";
		FileUtils.createDirs(path);

		PrintWriter mapXMLWriter = new PrintWriter(path + fileMapperName(sqlFile.name) + ".xml", "UTF-8");
		mapXMLWriter.printf("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
		mapXMLWriter.printf("<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\" >\n");
		mapXMLWriter.printf("\n");
		mapXMLWriter.printf("<mapper namespace=\"%s\">\n", package_name);
		mapXMLWriter.printf("\n");

		String fileName = path + fileMapperName(sqlFile.name) + ".java";
		PrintWriter mapperWriter = new PrintWriter(fileName, "UTF-8");
		mapperWriter.printf("package %s;", package_name);
		mapperWriter.printf("\n");

		mapperWriter.printf("import java.util.List;\n");
		mapperWriter.printf("import org.apache.ibatis.annotations.Delete;\n");
		mapperWriter.printf("import org.apache.ibatis.annotations.Insert;\n");
		mapperWriter.printf("import org.apache.ibatis.annotations.Options;\n");
		mapperWriter.printf("import org.apache.ibatis.annotations.Param;\n");
		mapperWriter.printf("import org.apache.ibatis.annotations.ResultMap;\n");
		mapperWriter.printf("import org.apache.ibatis.annotations.Select;\n");
		mapperWriter.printf("import org.apache.ibatis.annotations.Update;\n");
		mapperWriter.printf("import org.springframework.stereotype.Repository;\n");

		mapperWriter.printf("\n");
		mapperWriter.printf("@Repository\n");

		mapperWriter.printf("public interface %s {\n", fileMapperName(sqlFile.name));
		mapperWriter.printf("\n");

		for (Table table : sqlFile.tables) {
			PrintWriter javaWriter = new PrintWriter(path + tableClassName(table.name) + ".java", "UTF-8");
			writeTable(table, package_name, mapXMLWriter, javaWriter);
			javaWriter.close();

			String[] tns = table.name.split("_");

			String param = "";
			String method = "";
			for (int i = 0; i < tns.length; i++) {
				String v = tns[i];
				v = v.replaceFirst(v.substring(0, 1), v.substring(0, 1).toUpperCase());
				if (i == 0) {
					param = v.replaceFirst(v.substring(0, 1), v.substring(0, 1).toLowerCase());
					method = v;
					continue;
				}
				param += v;
				method += v;
			}
			{
				// insert
				mapperWriter.printf("	@Insert(\"insert into %s (", table.name);
				for (int i = 0; i < table.fields.size(); i++) {
					if (table.autoKey == null || !table.autoKey.equals(table.fields.get(i).name)) {
						mapperWriter.printf("%s", table.fields.get(i).name);
						if (i != table.fields.size() - 1) {
							mapperWriter.printf(", ");
						}
					}
				}
				mapperWriter.printf(") values (");
				for (int i = 0; i < table.fields.size(); i++) {
					if (table.autoKey == null || !table.autoKey.equals(table.fields.get(i).name)) {
						mapperWriter.printf("#{%s.%s}", param, table.fields.get(i).name);
						if (i != table.fields.size() - 1) {
							mapperWriter.printf(", ");
						}
					}
				}
				mapperWriter.printf(")\")\n");
				if (table.autoKey != null) {
					mapperWriter.printf("	@Options(useGeneratedKeys = true, keyProperty = \"%s\")\n", param + "." + table.autoKey);
				}

				mapperWriter.printf("	public int insert%s (@Param(\"%s\") %s %s);\n", method, param, tableClassName(table.name), param);
				mapperWriter.printf("\n");
			}
			{
				// update
				if (!table.primaryKeys.isEmpty()) {
					mapperWriter.printf("	@Update(\"update %s", table.name + " set ");
					for (int i = 0; i < table.fields.size(); i++) {
						if (!table.primaryKeys.contains(table.fields.get(i).name) && !table.fields.get(i).name.equals(VERSION)) {
							mapperWriter.printf("%s", table.fields.get(i).name);
							mapperWriter.printf(" = #{%s.%s}", param, table.fields.get(i).name);
							if (i != table.fields.size() - 1) {
								mapperWriter.printf(", ");
							}
						}
					}

					if (table.hasVersion) {
						mapperWriter.printf(", version = version + 1 ");
					}
					mapperWriter.printf(" where ");
					int k = 0;
					for (String key : table.primaryKeys) {
						if (k > 0) {
							mapperWriter.printf(" and ");
						}
						mapperWriter.printf(key + " = #{" + param + "." + key + "}");
						k++;
					}
					if (table.hasVersion) {
						mapperWriter.printf(", and version" + " = #{" + param + ".version}");
					}
					mapperWriter.printf("\")\n");
					mapperWriter.printf("	public int update%s (@Param(\"%s\") %s %s);\n", method, param, tableClassName(table.name), param);
					mapperWriter.printf("\n");
				}
				if (table.autoKey == null) {
					mapperWriter.printf("	@Update(\"replace into %s (", table.name + "");
					for (int i = 0; i < table.fields.size(); i++) {
						mapperWriter.printf("%s", table.fields.get(i).name);
						if (i != table.fields.size() - 1) {
							mapperWriter.printf(", ");
						}
					}
					mapperWriter.printf(") values (");
					for (int i = 0; i < table.fields.size(); i++) {
						mapperWriter.printf("#{%s.%s}", param, table.fields.get(i).name);
						if (i != table.fields.size() - 1) {
							mapperWriter.printf(", ");
						}
					}
					mapperWriter.printf(")\")\n");
					mapperWriter.printf("	public int set%s (@Param(\"%s\") %s %s);\n", method, param, tableClassName(table.name), param);
					mapperWriter.printf("\n");
				}
			}
			{
				// select
				if (table.hasPlayerId) {
					if (table.autoKey == null && table.primaryKeys.contains(PLAYER_ID) && table.primaryKeys.size() == 1) {
						mapperWriter.printf("	@Select(\"select * from %s where player_id = #{player_id}\")\n", table.name);
						mapperWriter.printf("	@ResultMap(\"%s\")\n", package_name + "." + tableMapperName(table.name));

						mapperWriter.printf("	public %s get%s (@Param(\"player_id\") long player_id );\n", tableClassName(table.name), method);
						mapperWriter.printf("\n");
					} else {
						mapperWriter.printf("	@Select(\"select * from %s where player_id = #{player_id}\")\n", table.name);
						mapperWriter.printf("	@ResultMap(\"%s\")\n", package_name + "." + tableMapperName(table.name));

						mapperWriter.printf("	public List<%s> get%ss (@Param(\"player_id\") long player_id );\n", tableClassName(table.name), method);
						mapperWriter.printf("\n");
					}
				}

				mapperWriter.printf("	@Select(\"select * from %s\")\n", table.name);
				mapperWriter.printf("	@ResultMap(\"%s\")\n", package_name + "." + tableMapperName(table.name));

				mapperWriter.printf("	public List<%s> getAll%s ();\n", tableClassName(table.name), method);
				mapperWriter.printf("\n");
			}
			{
				if (!table.primaryKeys.isEmpty()) {
					mapperWriter.printf("	@Delete(\"delete from %s where ", table.name);
					int k = 0;
					for (String key : table.primaryKeys) {
						if (k > 0) {
							mapperWriter.printf(" and ");
						}
						mapperWriter.printf(key + " = #{" + key + "}");
						k++;
					}
					mapperWriter.printf("\")\n");
					mapperWriter.printf("	public int delete%s (", method);
					k = 0;
					for (String key : table.primaryKeys) {
						if (k > 0) {
							mapperWriter.printf(", ");
						}
						mapperWriter.printf("@Param(\"%s\") %s %s", key, table.getJavaType(key), key	);
						k++;
					}
					mapperWriter.printf(");\n");
					mapperWriter.printf("\n");
				}
			}
		}
		mapXMLWriter.printf("</mapper>\n");
		mapXMLWriter.close();
		mapperWriter.printf("}\n");
		mapperWriter.close();
	}

	public static void copyMappers(String searchDir, String toDir) throws Exception {
		FileUtils.createDirs(toDir);
		List<File> xmlFiles = new ArrayList<File>();
		FileUtils.searchFile(new File(searchDir), ".xml", xmlFiles);
		for (File file : xmlFiles) {
			if (file.getName().endsWith("Mapper.xml")) {
				FileUtils.copyFile(file, toDir + file.getName());
			}
		}
	}
}
